package com.baoyouqun.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baoyouqun.API.IM.ImConfig;
import com.baoyouqun.API.IM.TLSSigAPIv2;
import com.baoyouqun.API.IM.TencentImService;
import com.baoyouqun.API.IM.entity.FriendStatus;
import com.baoyouqun.API.friend.FriendRelationService;
import com.baoyouqun.API.friend.domain.FriendRelation;
import com.baoyouqun.API.task.UserRecommendService;
import com.baoyouqun.base.SystemConstant;
import com.baoyouqun.base.utils.ConvertUtils;
import com.baoyouqun.base.utils.ExcelTool;
import com.baoyouqun.base.utils.PhoneNumberValidator;
import com.baoyouqun.domain.CreateCmd.BackUserCreateCmd;
import com.baoyouqun.domain.CreateCmd.UserCreateCmd;
import com.baoyouqun.domain.CreateCmd.UserResourceCreateCmd;
import com.baoyouqun.domain.Enum.DemandTypeEnum;
import com.baoyouqun.domain.Enum.ResourceTypeEnum;
import com.baoyouqun.domain.Query.UserDetailQuery;
import com.baoyouqun.domain.Query.UserQuery;
import com.baoyouqun.domain.UpdateCmd.BackUserUpdateCmd;
import com.baoyouqun.domain.UpdateCmd.UserUpdateCmd;
import com.baoyouqun.domain.VO.*;
import com.baoyouqun.domain.cmd.BatchDeleteUserCmd;
import com.baoyouqun.domain.cmd.ExportUserCmd;
import com.baoyouqun.domain.cmd.ResetPasswordCmd;
import com.baoyouqun.domain.cmd.UpdatePasswordCmd;
import com.baoyouqun.entity.*;
import com.baoyouqun.mapper.UserMapper;
import com.baoyouqun.mapper.UserVipTimeMapper;
import com.baoyouqun.service.*;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.StringUtil;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private UserRoleService userRoleService;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private RoleService roleService;
    @Resource
    private UserResourceService userResourceService;
    @Resource
    private UserVipTimeService userVipTimeService;
    @Resource
    private UserVipTimeMapper userVipTimeMapper;
    @Resource
    @Lazy
    private DemandService demandService;
    @Resource
    @Lazy
    private TencentImService tencentImService;
    @Resource
    private ImConfig imConfig;
    @Resource
    private FriendRelationService friendRelationService;
    @Resource
    @Lazy
    private CityService cityService;
    @Resource
    private UserRecommendService userRecommendService;
    @Resource
    private SystemConfigService systemConfigService;
    @Resource
    @Lazy
    private UserPointsService userPointsService;
    @Resource
    @Lazy
    private UserPointsRecordService userPointsRecordService;

    @Override
    public SingleResponse<UserVO> create(UserCreateCmd cmd) {
        // 1. 检查手机号是否已注册
        User existingUser = selectByCellphone(cmd.getCellphone());
        if (existingUser != null) {
            if (existingUser.getLogout()) {
                existingUser.setLogout(false);
                updateById(existingUser);
            }
            // 仅处理未绑定微信OpenID的用户
            if (StringUtil.isBlank(existingUser.getWxOpenid())) {
                boolean baoZhiHao = PhoneNumberValidator.isValidPhone(cmd.getCellphone());
                if (baoZhiHao) {
                    userVipTimeService.createBaoZhiHao(existingUser.getId());
                } else {
                    userVipTimeService.createTemp(existingUser.getId());
                }

                return updateExistingUser(existingUser, cmd);
            } else {
                return SingleResponse.buildFailure("30010", "该手机号已注册");
            }
        }

        // 2. 手机号未注册，执行新建逻辑
        return createNewUser(cmd);
    }

    @Override
    public SingleResponse<UserVO> createBackUser(BackUserCreateCmd cmd) {
        cmd.setBackWorker(true);
        User existingUser = selectByCellphone(cmd.getCellphone());
        if (existingUser != null && existingUser.getBackWorker() != null && existingUser.getBackWorker().equals(Boolean.TRUE)) {
            return SingleResponse.buildFailure("30010", "该手机号已注册");
        }
        User newUser = new User();
        if (existingUser != null) {
            newUser = existingUser;
            newUser.setBackWorker(true);
            updateById(newUser);
        } else {
            // 2. 手机号未注册，执行新建逻辑
            newUser = ConvertUtils.copyBean(cmd, User.class); // 新建时拷贝所有属性（包括空值，由业务默认值处理）
            // 处理密码（默认使用手机号作为密码）
            newUser.setPassword(passwordEncoder.encode(cmd.getCellphone()));
            if (!save(newUser)) {
                return SingleResponse.buildFailure("100101", "创建用户失败");
            }
        }

        // 先删除该用户的所有角色
        userRoleService.deleteByUserId(newUser.getId());

        // 批量添加新角色
        User finalNewUser = newUser;
        if (CollUtil.isNotEmpty(cmd.getRoleIds())) {
            List<UserRole> userRoles = cmd.getRoleIds().stream().map(roleId -> {
                UserRole userRole = new UserRole();
                userRole.setUserId(finalNewUser.getId());
                userRole.setRoleId(roleId);
                return userRole;
            }).collect(Collectors.toList());
            if (!userRoleService.saveBatch(userRoles)) {
                return SingleResponse.buildFailure("100101", "创建用户角色失败");
            }
        }
        return SingleResponse.of(convertToVO(newUser));
    }

    @Override
    public SingleResponse<UserVO> updateBackUser(BackUserUpdateCmd cmd) {
        User existingUser = getById(cmd.getId());
        if (existingUser == null) {
            return SingleResponse.buildFailure("30010", "用户不存在");
        }
        User newUser = existingUser;
        newUser = existingUser;
        newUser.setName(cmd.getName());
        updateById(newUser);

        // 先删除该用户的所有角色
        userRoleService.deleteByUserId(newUser.getId());

        // 批量添加新角色
        User finalNewUser = newUser;
        if (CollUtil.isNotEmpty(cmd.getRoleIds())) {
            List<UserRole> userRoles = cmd.getRoleIds().stream().map(roleId -> {
                UserRole userRole = new UserRole();
                userRole.setUserId(finalNewUser.getId());
                userRole.setRoleId(roleId);
                return userRole;
            }).collect(Collectors.toList());
            if (!userRoleService.saveBatch(userRoles)) {
                return SingleResponse.buildFailure("100101", "创建用户角色失败");
            }
        }
        return SingleResponse.of(convertToVO(newUser));
    }

    /**
     * 更新已有用户（仅更新cmd中非空属性）
     */
    private SingleResponse<UserVO> updateExistingUser(User existingUser, UserCreateCmd cmd) {
        boolean hasUpdated = false;

        // 逐个检查cmd属性，仅更新非空值
        if (StringUtil.isNotBlank(cmd.getWxOpenid())) {
            existingUser.setWxOpenid(cmd.getWxOpenid());
            hasUpdated = true;
        }
        // 可根据实际业务添加其他需要更新的属性（如昵称、头像等）
        if (StringUtil.isNotBlank(cmd.getName())) {
            existingUser.setName(cmd.getName());
            hasUpdated = true;
        }
        if (StringUtil.isNotBlank(cmd.getHeadImg())) {
            existingUser.setHeadImg(cmd.getHeadImg());
            hasUpdated = true;
        }
        if (hasUpdated) {
            if (!updateById(existingUser)) {
                return SingleResponse.buildFailure("30010", "更新用户信息失败");
            }
        }

        return SingleResponse.of(convertToVO(existingUser));
    }

    /**
     * 创建新用户
     */
    private SingleResponse<UserVO> createNewUser(UserCreateCmd cmd) {
        User newUser = ConvertUtils.copyBean(cmd, User.class); // 新建时拷贝所有属性（包括空值，由业务默认值处理）
        // 处理密码（默认使用手机号作为密码）
        String password = StringUtil.isBlank(cmd.getPassword())
                ? cmd.getCellphone()
                : cmd.getPassword();
        newUser.setPassword(passwordEncoder.encode(password));
        boolean baoZhiHao = PhoneNumberValidator.isValidPhone(cmd.getCellphone());
        newUser.setBaoZhiHao(baoZhiHao);
        if (!save(newUser)) {
            return SingleResponse.buildFailure("100101", "创建用户失败");
        }


        userVipTimeService.createTemp(newUser.getId());
        return SingleResponse.of(convertToVO(newUser));
    }

    /**
     * 实体转换工具方法
     */
    private UserVO convertToVO(User user) {
        UserVO vo =
                ConvertUtils.copyBean(user, UserVO.class);
        return vo;
    }

    @Override
    public SingleResponse<String> register(UserCreateCmd cmd) {
        // 调用用户服务创建用户
        SingleResponse<UserVO> response = create(cmd);
        if (!response.isSuccess()) {
            return SingleResponse.buildFailure("30002", "注册失败" + response.getErrMessage());
        }

        // 注册成功后自动登录
        User user = selectByCellphone(cmd.getCellphone());
        StpUtil.login(user.getId());
        String token = StpUtil.getTokenValue();
        // 4. 将用户信息存入当前会话（未用Redis时，会话存在内存中）
        StpUtil.getSession().set(SystemConstant.SA_TOKEN_USER_INFO, ConvertUtils.copyBean(user, UserVO.class));
        StpUtil.getSession().set(SystemConstant.SA_TOKEN_USER_ID, user.getId());

        if (StringUtils.isNotBlank(cmd.getInviteCellphone())) {
            InviteFriendDTO inviteFriendDTO = systemConfigService.getInviteFriend();
            if (inviteFriendDTO != null && inviteFriendDTO.getEnabled() && Objects.nonNull(inviteFriendDTO.getRewardAmount())) {
                // 1. 根据邀请手机号查询发起邀请的用户（邀请人）
                User inviterUser = selectByCellphone(cmd.getInviteCellphone());
                if (Objects.isNull(inviterUser)) {
                    return SingleResponse.of(token);
                }
                String inviterUserId = inviterUser.getId();
                Long rewardPoints = inviteFriendDTO.getRewardAmount(); // 邀请奖励积分（正数）
                int intRewardPoints = Math.toIntExact(rewardPoints);

                // 2. 查询邀请人的积分记录，无则初始化
                UserPointsVO inviterPointsVO = userPointsService.getByUserId(inviterUserId);
                UserPoints inviterPoints;
                int pointsBefore;
                int pointsAfter;

                if (Objects.isNull(inviterPointsVO)) {

                    // 2.1 初始化积分记录
                    inviterPoints = new UserPoints();
                    inviterPoints.setUserId(inviterUserId);
                    inviterPoints.setTotalPoints(Math.toIntExact(intRewardPoints)); // 总积分初始化为奖励积分
                    inviterPoints.setCurrentPoints(Math.toIntExact(intRewardPoints)); // 当前可用积分
                    inviterPoints.setFrozenPoints(0);
                    inviterPoints.setCreateTime(LocalDateTime.now());
                    inviterPoints.setUpdateTime(LocalDateTime.now());
                    pointsBefore = 0;
                    pointsAfter = intRewardPoints;
                } else {
                    // 2.2 已有积分记录，计算变动后积分
                    inviterPoints = ConvertUtils.copyBean(inviterPointsVO, UserPoints.class);
                    pointsBefore = inviterPoints.getCurrentPoints();
                    pointsAfter = pointsBefore + intRewardPoints;
                    inviterPoints.setCurrentPoints(pointsAfter);
                    inviterPoints.setTotalPoints(inviterPoints.getTotalPoints() + intRewardPoints); // 总积分累加
                    inviterPoints.setUpdateTime(LocalDateTime.now());
                }

                // 3. 创建积分变动记录（类型：邀请奖励，假设changeType=4）
                UserPointsRecord rewardRecord = new UserPointsRecord();
                rewardRecord.setUserId(inviterUserId);
                rewardRecord.setChangePoints(intRewardPoints); // 正数表示增加
                rewardRecord.setChangeType((byte) 4); // 假设4代表邀请好友奖励
                rewardRecord.setBizDesc("邀请好友获得积分奖励");
                rewardRecord.setPointsBeforeChange(pointsBefore);
                rewardRecord.setPointsAfterChange(pointsAfter);
                rewardRecord.setCreateTime(LocalDateTime.now());
                rewardRecord.setOperator("system"); // 系统操作
                rewardRecord.setType("invite_reward"); // 业务类型标识

                // 4. 事务内执行：保存积分记录 + 保存变动记录
                try {
                    // 保存/更新用户积分
                    boolean saveOrUpdateSuccess = Objects.isNull(inviterPointsVO)
                            ? userPointsService.save(inviterPoints)
                            : userPointsService.updateById(inviterPoints);
                    if (!saveOrUpdateSuccess) {
                        throw new RuntimeException("邀请人积分记录更新失败");
                    }

                    // 保存积分变动记录
                    boolean recordSuccess = userPointsRecordService.save(rewardRecord);
                } catch (Exception e) {
                    log.error("处理邀请奖励积分异常", e);
                }
            }
        }

        return SingleResponse.of(token);
    }

    @Override
    public Response update(UserUpdateCmd cmd) {
        if (Objects.isNull(cmd)) {
            return Response.buildFailure("100301", "参数不能为空");
        }
        if (StringUtil.isBlank(cmd.getId())) {
            return Response.buildFailure("100302", "ID不能为空");
        }
        User oldEntity = getById(cmd.getId());
        if (Objects.isNull(oldEntity)) {
            return Response.buildFailure("100303", "未查询到此数据");
        }
        // 替换copyBean为手动set属性
        oldEntity.setName(cmd.getName());
        oldEntity.setHeadImg(cmd.getHeadImg());
        oldEntity.setExtraPhones(cmd.getExtraPhones());
        oldEntity.setSex(cmd.getSex());
        oldEntity.setCompany(cmd.getCompany());
        oldEntity.setJob(cmd.getJob());
        oldEntity.setEducation(cmd.getEducation());
        oldEntity.setIndustry(cmd.getIndustry());
        oldEntity.setCityCode(cmd.getCityCode());
        oldEntity.setProvince(cmd.getProvince());
        oldEntity.setCity(cmd.getCity());

        if (Objects.nonNull(cmd.getDistrict())) {
            oldEntity.setDistrict(cmd.getDistrict());
        }
        if (Objects.nonNull(cmd.getLogout())) {
            oldEntity.setLogout(cmd.getLogout());
        }
        if (Objects.nonNull(cmd.getDisEnable())) {
            oldEntity.setDisEnable(cmd.getDisEnable());
        }
        oldEntity.setIsPublicResource(cmd.getIsPublicResource());
        boolean result = updateById(oldEntity);
        if (result) {
            if (!Objects.equals(cmd.getHeadImg(), oldEntity.getHeadImg())) {
                // 1. 获取关键参数（用户ID、新头像URL）
                String userId = oldEntity.getId(); // 当前修改头像的用户ID（需与腾讯IM的UserId一致）
                String newHeadImgUrl = cmd.getHeadImg(); // 新头像的完整URL（需公网可访问，腾讯IM不存储图片，仅存URL）

                try {
                    tencentImService.syncUserHeadImg(userId, newHeadImgUrl);
                } catch (Exception e) {
                    log.error("用户[{}]头像同步腾讯IM异常，新头像URL：{}", userId, newHeadImgUrl, e);
                }
            }

            return Response.buildSuccess();
        } else {
            return Response.buildFailure("100304", "更新失败");
        }
    }

    // 正确的更新session_key方法
    @Override
    public boolean updateSessionKey(String openid, String sessionKey) {
        // 1. 使用LambdaUpdateWrapper（更新专用构造器）
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();

        // 2. 设置更新条件和字段（变量名与引用保持一致）
        updateWrapper.eq(User::getWxOpenid, openid)  // 条件：wx_openid = openid
                .set(User::getWxSessionKey, sessionKey)  // 更新session_key
                .set(User::getWxSessionExpire, LocalDateTime.now().plusHours(2));  // 更新过期时间

        // 3. 执行更新：第一个参数传null（字段已通过wrapper设置），返回受影响的行数
        return update(updateWrapper);
    }

    @Override
    public Response setUserInfo(UserUpdateCmd userInfo, List<UserResourceCreateCmd> resourceList) {
        // 2. 保存/更新个人信息（这里简单示例，若用户已有记录则更新，否则插入；实际需结合登录态获取用户ID ）
        Response response = update(userInfo);
        if (!response.isSuccess()) {
            return Response.buildFailure("100305", "保存个人信息失败");
        }

        // 删除旧关联
        userResourceService.deleteByUserId(userInfo.getId());
        userResourceService.handleResource(userInfo.getId(), resourceList);

        return response;
    }

    @Override
    public Response disEnable(UserUpdateCmd cmd) {
        if (Objects.isNull(cmd)) {
            return Response.buildFailure("100301", "参数不能为空");
        }
        User oldEntity = getById(cmd.getId());
        if (Objects.isNull(oldEntity)) {
            return Response.buildFailure("100303", "未查询到此数据");
        }
        oldEntity.setDisEnable(cmd.getDisEnable());
        boolean result = updateById(oldEntity);
        if (result) {
            return Response.buildSuccess();
        } else {
            return Response.buildFailure("100305", "禁用失败");
        }
    }

    @Override
    public Response setUserExtraPhones(String userId, List<String> extraPhones) {
        User oldEntity = getById(userId);
        if (Objects.isNull(oldEntity)) {
            return Response.buildFailure("100303", "未查询到此数据");
        }

        oldEntity.setExtraPhones(extraPhones);
        boolean result = updateById(oldEntity);
        if (result) {
            return Response.buildSuccess();
        } else {
            return Response.buildFailure("100305", "更新联系方式失败");
        }
    }

    @Override
    public UserVO selectById(String id) {
        User entity = userMapper.selectById(id);
        UserVO vo =
                ConvertUtils.copyBean(entity, UserVO.class);
        if (Objects.isNull(vo)) {
            return vo;
        }
        String memberLevel = "";
        UserVipTimeVO currentVip = userVipTimeService.getCurrentVip(id);
        if (currentVip != null) {
            // 获取会员等级名称
            memberLevel = currentVip.getVipLevelName();
        }
        vo.setMemberLevelName(memberLevel);
        return vo;
    }

    @Override
    public UserVO userDetail(String id) {
        User entity = userMapper.selectById(id);
        UserVO vo = ConvertUtils.copyBean(entity, UserVO.class);

        if (vo == null) {
            return null;
        }
        //会员
        String memberLevel = "";
        UserVipTimeVO currentVip = userVipTimeService.getCurrentVip(id);
        if (currentVip != null) {
            // 获取会员等级名称
            memberLevel = currentVip.getVipLevelName();
        }
        vo.setMemberLevelName(memberLevel);

        //资源
        List<UserResourceVO> resourceVOList = userResourceService.selectByUserId(vo.getId());
        vo.setResourceVOList(resourceVOList);

        return vo;


    }

    @Override
    public IPage<UserVO> pageVo(UserQuery query) {
        IPage<User> page = new Page<>(query.getCurrent(), query.getSize());
        page = page(page,
                new LambdaQueryWrapper<User>()
                        .like(StringUtil.isNotBlank(query.getName()), User::getName, query.getName())
                        .like(StringUtil.isNotBlank(query.getCellphone()), User::getCellphone, query.getCellphone())
                        .eq(Objects.nonNull(query.getDisEnable()), User::getDisEnable, query.getDisEnable())
        );
        Page<UserVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        List<UserVO> records = ConvertUtils.copyBeanList(page.getRecords(), UserVO.class);
        voPage.setRecords(records);
        return voPage;
    }

    @Override
    public IPage<UserDetailVO> backUserPage(UserQuery query) {

        // 1. 查询用户分页数据（基础信息）
        IPage<User> userPage = new Page<>(query.getCurrent(), query.getSize());
        userPage = page(userPage,
                new LambdaQueryWrapper<User>()
                        .eq(User::getBackWorker, true)
                        .like(StringUtil.isNotBlank(query.getName()), User::getName, query.getName())
                        .like(StringUtil.isNotBlank(query.getCellphone()), User::getCellphone, query.getCellphone())
                        .eq(Objects.nonNull(query.getDisEnable()), User::getDisEnable, query.getDisEnable())
        );
        List<User> userList = userPage.getRecords();
        if (CollUtil.isEmpty(userList)) {
            return new Page<>(query.getCurrent(), query.getSize(), 0);
        }

        // 2. 提取所有用户ID，批量查询角色关联
        List<String> userIds = userList.stream()
                .map(User::getId)
                .collect(Collectors.toList());

        // 2.1 批量查询用户-角色映射关系（用户ID -> 角色ID列表）
        Map<String, List<String>> userRoleMap = userRoleService.selectByUserIds(userIds);

        // 2.2 收集所有角色ID，批量查询角色信息并转换为RoleVO
        Set<String> allRoleIds = userRoleMap.values().stream()
                .flatMap(List::stream)
                .collect(Collectors.toSet());
        List<Role> roleList = roleService.listByIds(allRoleIds);
        // 角色ID -> RoleVO映射（直接转换为VO，保留完整角色信息）
        Map<String, RoleVO> roleIdToVOMap = roleList.stream()
                .map(role -> ConvertUtils.copyBean(role, RoleVO.class))
                .collect(Collectors.toMap(RoleVO::getId, roleVO -> roleVO));

        // 3. 转换为UserDetailVO并设置角色VO列表
        List<UserDetailVO> detailVOList = userList.stream().map(user -> {
            // 获取当前用户的角色ID列表
            List<String> roleIds = userRoleMap.getOrDefault(user.getId(), new ArrayList<>());
            // 转换为RoleVO列表（过滤无效角色）
            List<RoleVO> roleVOs = roleIds.stream()
                    .map(roleIdToVOMap::get)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            // 调用转换方法
            return fromUser(user, roleVOs);
        }).collect(Collectors.toList());

        // 4. 构建分页结果
        IPage<UserDetailVO> detailPage = new Page<>(
                userPage.getCurrent(),
                userPage.getSize(),
                userPage.getTotal()
        );
        detailPage.setRecords(detailVOList);
        return detailPage;
    }

    public UserDetailVO fromUser(User user, List<RoleVO> roleVOs) {
        if (user == null) {
            return null;
        }
        UserDetailVO detailVO = new UserDetailVO();
        detailVO.setUserId(user.getId());
        detailVO.setRealName(user.getName()); // 映射真实姓名
        detailVO.setCellphone(user.getCellphone());
        detailVO.setDisEnable(user.getDisEnable()); // 映射状态
        detailVO.setRoles(roleVOs); // 直接设置角色VO列表（不再是字符串列表）
        return detailVO;
    }

    @Override
    public User selectByCellphone(String cellphone) {
        return getOne(
                new LambdaQueryWrapper<User>()
                        .select(User::getId, User::getCellphone, User::getPassword, User::getWxOpenid, User::getBackWorker)
                        .eq(User::getCellphone, cellphone)
                        .last("limit 1")
        );
    }

    @Override
    public User selectByWxOpenid(String openid) {
        return getOne(
                new LambdaQueryWrapper<User>()
                        .eq(User::getWxOpenid, openid)
                        .last("limit 1")
        );
    }

    @Override
    public UserHomeVO userHome(String userId) {
        // 1. 根据userId查询用户核心信息（UserVO）
        UserVO userVO = selectById(userId);

        // 2. 边界处理：用户不存在时返回null（或根据业务抛ResourceNotFoundException）
        if (Objects.isNull(userVO)) {
            return null;
        }
        UserVipTimeVO userVipTimeVO = userVipTimeService.getCurrentVip(userId);
        userVO.setMemberLevelName(userVipTimeVO != null ? "VIP会员" : "临时会员");
        List<UserResourceVO> resourceVOList = userResourceService.selectByUserId(userId);
        userVO.setResourceVOList(resourceVOList);


        // 3. 初始化UserHomeVO并映射基础字段
        UserHomeVO userHomeVO = new UserHomeVO();

        // 3.1 基础信息映射（字段一一对应，特殊字段单独处理）
        userHomeVO.setHeadImg(userVO.getHeadImg()); // 头像
        userHomeVO.setUserName(userVO.getName());   // 用户名
        userHomeVO.setSex(userVO.getSex());         // 性别
        userHomeVO.setCompany(userVO.getCompany()); // 公司
        userHomeVO.setIndustry(userVO.getIndustry()); // 行业
        userHomeVO.setEducation(userVO.getEducation()); // 学历
        userHomeVO.setPosition(userVO.getJob());    // UserVO的job对应UserHomeVO的position
        userHomeVO.setImUserSig(userVO.getImUserSig());

        // 3.2 处理“是否为VIP”：假设memberLevel为"VIP"时判定为VIP（可根据实际业务调整，如memberLevelName包含"VIP"）
        boolean isVip = userVipTimeVO != null;
        userHomeVO.setIsVip(isVip);

        // 3.3 处理“地区”：拼接省+市+区县（避免空值导致的"null"字符串）
        String province = Objects.isNull(userVO.getProvince()) ? "" : userVO.getProvince();
        String city = Objects.isNull(userVO.getCity()) ? "" : userVO.getCity();
        String district = Objects.isNull(userVO.getDistrict()) ? "" : userVO.getDistrict();
        userHomeVO.setRegion(province + city + district); // 示例：江西省南昌市青山湖区

        // 4. 处理“提供的资源”和“需要的资源”（从UserVO的resourceVOList过滤）
        List<UserResourceVO> allResources = userVO.getResourceVOList();
        if (Objects.isNull(allResources)) {
            // 若资源列表为空，赋值空集合避免NPE
            userHomeVO.setProvideResources(Collections.emptyList());
            userHomeVO.setNeedResources(Collections.emptyList());
            return userHomeVO;
        }

        // 4.1 过滤“提供的资源”（resourceType = "PROVIDE"）
        List<UserResourceVO> provideResources = allResources.stream()
                .filter(resource -> Objects.nonNull(resource.getResourceTypeEnum())
                        && ResourceTypeEnum.OWN.equals(resource.getResourceTypeEnum()))
                .collect(Collectors.toList());

        // 4.2 过滤“需要的资源”（resourceType = "NEED"）
        List<UserResourceVO> needResources = allResources.stream()
                .filter(resource -> Objects.nonNull(resource.getResourceTypeEnum())
                        && ResourceTypeEnum.NEED.equals(resource.getResourceTypeEnum()))
                .collect(Collectors.toList());

        List<DemandVO> demandVOList = demandService.selectByUserId(userId);

        // 4.3 赋值资源列表
        userHomeVO.setProvideResources(provideResources);
        userHomeVO.setNeedResources(needResources);
        List<DemandVO> ownDemands = demandVOList.stream()
                .filter(demand -> Objects.nonNull(demand.getType())
                        && DemandTypeEnum.OWN.equals(demand.getType()))
                .collect(Collectors.toList());

        List<DemandVO> needDemands = demandVOList.stream()
                .filter(demand -> Objects.nonNull(demand.getType())
                        && DemandTypeEnum.NEED.equals(demand.getType()))
                .collect(Collectors.toList());

        // 4.3 赋值资源列表
        userHomeVO.setOwnDemands(ownDemands);
        userHomeVO.setNeedDemands(needDemands);

        // 5. 返回组装完成的UserHomeVO
        return userHomeVO;
    }

    @Override
    public boolean existsByWxOpenid(String wxOpenid) {
        return count(
                new LambdaQueryWrapper<User>()
                        .eq(User::getWxOpenid, wxOpenid)
                        .last("limit 1")
        ) > 0;
    }

    @Override
    public boolean existsByCellphone(String cellphone) {
        return count(
                new LambdaQueryWrapper<User>()
                        .eq(User::getCellphone, cellphone)
                        .last("limit 1")
        ) > 0;
    }

    @Override
    public List<Map<String, Object>> countByAreaCodes() {
        return userMapper.countByAreaCodes();
    }

    @Override
    public SingleResponse<List<RoleVO>> getUserRoles(String userId) {
        List<String> roleIdList = userRoleService.selectByUserId(userId);

        List<Role> roleList = roleService.listByIds(roleIdList);
        return SingleResponse.of(ConvertUtils.copyBeanList(roleList, RoleVO.class));
    }

    // 1. 批量删除用户
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response batchDelete(BatchDeleteUserCmd cmd) {
        for (String userId : cmd.getUserIds()) {
            // 逻辑判断：检查用户是否存在
            User user = getById(userId);
            // 逻辑判断：超级管理员不能删除
            if ("1".equals(user.getId())) {
                return Response.buildFailure("200401", "超级管理员不能删除");
            }
        }
        if (removeByIds(cmd.getUserIds())) {
            return Response.buildSuccess();
        }

        return Response.buildFailure("200402", "批量删除失败");
    }

    // 2. 重置用户密码
    @Override
    public Response resetPassword(ResetPasswordCmd cmd) {
        // 逻辑判断：用户是否存在
        User user = getById(cmd.getUserId());
        if (user == null) {
            return Response.buildFailure("300402", "用户不存在");
        }
        // 加密密码并更新
        user.setPassword(passwordEncoder.encode(cmd.getNewPassword()));
        if (updateById(user)) {
            return Response.buildSuccess();
        }
        return Response.buildFailure("300402", "密码重置失败");
    }

    // 3. 导出用户列表（Hutool实现）
    @Override
    public void exportUser(ExportUserCmd cmd, HttpServletResponse response) throws IOException {
        // 1. 构建查询条件（保留原筛选逻辑）
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(cmd.getName()), User::getName, cmd.getName())
                .like(StringUtils.isNotBlank(cmd.getCellphone()), User::getCellphone, cmd.getCellphone())
                .eq(cmd.getDisEnable() != null, User::getDisEnable, cmd.getDisEnable())
                .eq(User::getBackWorker, true); // 只导出后台用户（与页面数据范围一致）

        // 2. 查询用户基础数据 + 批量关联角色（避免N+1查询，参考backUserPage逻辑）
        List<User> userList = list(queryWrapper);
        if (CollUtil.isEmpty(userList)) {
            // 无数据时返回空Excel（避免报错）
            ExcelWriter writer = ExcelUtil.getWriter(true);
            setExcelResponseHeader(response, "用户列表");
            ServletOutputStream out = response.getOutputStream();
            writer.flush(out, true);
            out.close();
            writer.close();
            return;
        }

        // 2.1 批量查询用户-角色映射（用户ID → 角色ID列表）
        List<String> userIds = userList.stream().map(User::getId).collect(Collectors.toList());
        Map<String, List<String>> userRoleMap = userRoleService.selectByUserIds(userIds);

        // 2.2 批量查询角色信息（角色ID → 角色名称）
        Set<String> allRoleIds = userRoleMap.values().stream().flatMap(List::stream).collect(Collectors.toSet());
        List<Role> roleList = roleService.listByIds(allRoleIds);
        Map<String, String> roleIdToNameMap = roleList.stream()
                .collect(Collectors.toMap(Role::getId, Role::getRoleName, (a, b) -> b)); // 防重复角色ID

        // 3. 组装导出数据（与页面列完全匹配，用Map避免修改UserVO）
        List<Map<String, Object>> exportData = userList.stream().map(user -> {
            Map<String, Object> row = new HashMap<>();
            UserVO userVO = ConvertUtils.copyBean(user, UserVO.class);

            // 3.1 姓名（页面列1）
            row.put("name", userVO.getName());
            // 3.2 账号ID（页面列2）
            row.put("id", userVO.getId());
            // 3.3 手机号（页面列3）
            row.put("cellphone", userVO.getCellphone());
            // 3.4 角色（页面列4：多角色用逗号分隔）
            List<String> roleIds = userRoleMap.getOrDefault(user.getId(), new ArrayList<>());
            String roleNames = roleIds.stream()
                    .map(roleIdToNameMap::get)
                    .filter(Objects::nonNull)
                    .collect(Collectors.joining(",")); // 示例："超级管理员,运营人员"
            row.put("roleName", roleNames);
            // 3.5 状态（页面列5）
            row.put("disEnableName", userVO.getDisEnableName());

            return row;
        }).collect(Collectors.toList());

        // 4. Hutool Excel写入（列名、顺序与页面一致）
        ExcelWriter writer = ExcelUtil.getWriter(true);
        // 4.1 设置表头别名（键=Map的key，值=Excel表头名）
        writer.addHeaderAlias("name", "姓名");
        writer.addHeaderAlias("id", "账号ID");
        writer.addHeaderAlias("cellphone", "手机号");
        writer.addHeaderAlias("roleName", "角色");
        writer.addHeaderAlias("disEnableName", "状态");
        // 4.2 强制指定导出列顺序（确保与页面列顺序完全一致）
        writer.setOnlyAlias(true); // 只导出指定别名的列

        // 5. 写入数据并响应下载
        writer.write(exportData, true);
        setExcelResponseHeader(response, "后台用户列表"); // 封装响应头（复用逻辑）

        // 6. 关闭流（避免内存泄漏）
        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        out.close();
        writer.close();
    }

    /**
     * 封装Excel下载响应头（复用方法，避免重复代码）
     */
    private void setExcelResponseHeader(HttpServletResponse response, String fileNamePrefix) throws IOException {
        // 1. 设置响应类型
        response.setContentType("application/vnd.ms-excel;charset=utf-8");        // 2. 生成带时间戳的文件名（避免重复）
        // 2. 生成带时间戳的文件名（避免浏览器缓存重复文件）
        String fileName = URLEncoder.encode(fileNamePrefix + "_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")), StandardCharsets.UTF_8);
        // 3. 设置下载头

        ExcelTool.setExportFileName(response, "后台用户列表");
        // 4. 禁用缓存
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
    }

    // 4. 查询用户详情（含角色和权限）
    @Override
    public SingleResponse<UserDetailVO> getUserDetail(UserDetailQuery query) {
        UserDetailVO detailVO = new UserDetailVO();

        // 查询用户基本信息
        User user = getById(query.getUserId());
        if (user == null) {
            return SingleResponse.buildFailure("40031", "用户不存在");
        }
        detailVO.setUserId(user.getId());
        detailVO.setRealName(user.getName()); // 假设User中真实姓名字段为name
        detailVO.setCellphone(user.getCellphone());
        detailVO.setDisEnable(user.getDisEnable()); // 假设User中状态字段为disEnable

        // 查询用户角色
        List<RoleVO> roles = userRoleService.selectByUserId(query.getUserId()).stream()
                .map(roleId -> ConvertUtils.copyBean(roleService.getById(roleId), RoleVO.class))
                .collect(Collectors.toList());
        detailVO.setRoles(roles);

//        // 查询用户权限（假设角色-权限关联查询逻辑）
//        List<String> permissions = roleService.getPermissionsByRoleIds(
//                roles.stream().map(RoleVO::getId).collect(Collectors.toList()));
//        detailVO.setPermissions(permissions);

        return SingleResponse.of(detailVO);
    }

    // 5. 导入用户（Hutool解析Excel）
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SingleResponse<ImportUserVO> importUser(MultipartFile file) throws IOException {
        ImportUserVO result = new ImportUserVO();
        List<String> failReasons = new ArrayList<>();

        // 1. Hutool 解析Excel
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
        // 忽略表头，从第2行开始读（索引0为表头）
        List<List<Object>> rows = reader.read(1);
        result.setTotalCount(rows.size());

        // 2. 逐条处理数据
        int successCount = 0;
        for (int i = 0; i < rows.size(); i++) {
            List<Object> row = rows.get(i);
            int rowNum = i + 2; // 实际行数（从2开始）

            try {
                // 校验必填字段（示例：手机号在第2列，姓名在第1列）
                if (row.size() < 2 || row.get(1) == null) {
                    failReasons.add("第" + rowNum + "行：手机号不能为空");
                    continue;
                }
                String cellphone = row.get(1).toString().trim();
                if (existsByCellphone(cellphone)) {
                    failReasons.add("第" + rowNum + "行：手机号已存在");
                    continue;
                }

                // 构建用户对象
                User user = new User();
                user.setName(row.get(0) != null ? row.get(0).toString().trim() : "");
                user.setCellphone(cellphone);
                user.setPassword(passwordEncoder.encode(cellphone)); // 默认密码为手机号
                user.setDisEnable(true); // 默认启用

                // 保存用户
                if (save(user)) {
                    successCount++;
                } else {
                    failReasons.add("第" + rowNum + "行：保存失败");
                }
            } catch (Exception e) {
                failReasons.add("第" + rowNum + "行：处理异常-" + e.getMessage());
            }
        }

        // 3. 封装结果
        result.setSuccessCount(successCount);
        result.setFailCount(rows.size() - successCount);
        result.setFailReasons(failReasons);
        return SingleResponse.of(result);
    }

    // 6. 修改密码（验证旧密码）
    @Override
    public Response updatePassword(UpdatePasswordCmd cmd) {
        // 逻辑判断：用户是否存在
        User user = getById(StpUtil.getLoginIdAsString());
        if (user == null) {
            return Response.buildFailure("100431", "用户不存在");
        }

        // 验证旧密码
        if (!passwordEncoder.matches(cmd.getOldPassword(), user.getPassword())) {
            return Response.buildFailure("100432", "旧密码不正确");
        }

        // 更新新密码
        user.setPassword(passwordEncoder.encode(cmd.getNewPassword()));
        if (updateById(user)) {
            return Response.buildSuccess();
        }
        return Response.buildFailure("300402", "密码修改失败");
    }

    /**
     * 根据姓名、手机号、地区代码筛选用户ID列表
     */
    @Override
    public List<String> getUserIdListByConditions(String name, String cellphone, String cityCode) {
        // 构建查询条件：仅查ID，排除禁用/注销用户
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .select(User::getId)
                .eq(User::getDisEnable, false) // 未禁用
                .eq(User::getLogout, false);   // 未注销

        // 姓名模糊匹配
        if (StringUtil.isNotBlank(name)) {
            wrapper.like(User::getName, name);
        }
        // 手机号精确匹配（手机号唯一）
        if (StringUtil.isNotBlank(cellphone)) {
            wrapper.eq(User::getCellphone, cellphone);
        }
        // 地区代码精确匹配
        if (StringUtil.isNotBlank(cityCode)) {
            wrapper.eq(User::getCityCode, cityCode);
        }

        // 转换为String类型ID列表
        List<Object> userIdObjList = userMapper.selectObjs(wrapper);
        if (CollUtil.isEmpty(userIdObjList)) {
            return List.of();
        }
        return userIdObjList.stream()
                .map(obj -> (String) obj)
                .collect(Collectors.toList());
    }

    @Override
    public List<String> listUserIdsByName(String name) {
        // 1. 参数判空：名称为空时返回空列表
        if (StringUtils.isBlank(name)) {
            return CollUtil.newArrayList();
        }

        // 2. 模糊查询用户名称，只返回用户ID
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(User::getName, name) // 模糊匹配发布人名称
                .select(User::getId); // 仅查询ID字段，减少数据传输

        // 3. 执行查询并返回ID列表（无数据时返回空列表）
        List<User> userList = list(wrapper);
        return CollUtil.isEmpty(userList)
                ? CollUtil.newArrayList()
                : userList.stream().map(User::getId).collect(Collectors.toList());
    }

    @Override
    public List<String> listUserIdsByCellphone(String phoneNumber) {
        // 1. 参数判空：名称为空时返回空列表
        if (StringUtils.isBlank(phoneNumber)) {
            return CollUtil.newArrayList();
        }

        // 2. 模糊查询用户名称，只返回用户ID
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(User::getCellphone, phoneNumber) // 模糊匹配发布人名称
                .select(User::getId); // 仅查询ID字段，减少数据传输

        // 3. 执行查询并返回ID列表（无数据时返回空列表）
        List<User> userList = list(wrapper);
        return CollUtil.isEmpty(userList)
                ? CollUtil.newArrayList()
                : userList.stream().map(User::getId).collect(Collectors.toList());
    }

    @Override
    public List<UserVO> listByNameOrPhone(String keyword) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(User::getName, keyword)
                .or()
                .like(User::getCellphone, keyword);
        List<User> userList = this.list(wrapper);
        if (CollUtil.isEmpty(userList)) {
            return Collections.emptyList();
        }
        return ConvertUtils.copyBeanList(userList, UserVO.class);
    }

    @Override
    public boolean bindImId(String userId, String imId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        // 将IM id存入imUserSig字段
        user.setImUserSig(imId);
        return updateById(user);
    }

    @Override
    public IPage<PublicFriendVO> pagePublicFriends(String loginUserId, Long current, Long size,
                                                   Boolean isOnline, Boolean isVip, Boolean sameRegion, String cityCode) {
        // 1. 构建基础查询条件：公开、未禁用、未注销、排除自己
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                .eq(User::getIsPublic, true)          // 仅公开用户
                .eq(User::getDisEnable, false)        // 未禁用
                .eq(User::getLogout, false)           // 未注销
                .ne(User::getId, loginUserId);        // 排除当前登录用户
        if (Objects.equals(Boolean.TRUE, isOnline)) {
            Set<String> onlineUserIds = tencentImService.getOnlineUserIds();
            if (CollUtil.isEmpty(onlineUserIds)) {
                return new Page<>(current, size, 0);
            }
            queryWrapper.in(User::getId, onlineUserIds);
        }


        // 2. 补充筛选条件：VIP
        if (Objects.equals(Boolean.TRUE, isVip)) {
            // 先查询所有VIP用户ID（关联UserVipTime，当前有效的会员）
            List<String> vipUserIds = userVipTimeService.list(new LambdaQueryWrapper<UserVipTime>()
                            .gt(UserVipTime::getEndTime, LocalDateTime.now()) // 会员未过期
                    ).stream()
                    .map(UserVipTime::getUserId)
                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(vipUserIds)) {
                queryWrapper.in(User::getId, vipUserIds);
            } else {
                // 无VIP用户时，返回空分页
                return new Page<>(current, size, 0);
            }
        }

        // 3. 补充筛选条件：老乡（同省或同市）
        if (Objects.equals(Boolean.TRUE, sameRegion)) {
            User loginUser = getById(loginUserId);
            if (loginUser != null) {
                String loginProvince = loginUser.getProvince();
                String loginCity = loginUser.getCity();
                // 同省：优先同市，无市则同省
                if (StringUtils.isNotBlank(loginCity)) {
                    queryWrapper.eq(User::getCity, loginCity);
                } else if (StringUtils.isNotBlank(loginProvince)) {
                    queryWrapper.eq(User::getProvince, loginProvince);
                }
            }
        }
        if (StringUtil.isNotBlank(cityCode)) {
            List<String> codeList = cityService.getSingleWithChildren(cityCode);
            if (CollUtil.isNotEmpty(codeList)) {
                queryWrapper.in(User::getCityCode, codeList);
            } else {
                return new Page<>(current, size, 0);
            }
        }

        // 新增逻辑：查询当前用户的推荐好友ID，并设置排序优先级
        // 4. 获取当前用户的推荐好友ID列表
        List<String> recommendUserIds = userRecommendService.getRecommendUserIds(loginUserId);
        // 5. 构建排序逻辑：推荐好友排在前面（在推荐列表中=1，不在=0，降序）
        if (CollUtil.isNotEmpty(recommendUserIds)) {
            // 将推荐好友ID列表转为逗号分隔的字符串（用于SQL的IN条件）
            String recommendIdsStr = recommendUserIds.stream()
                    .collect(Collectors.joining("','", "'", "'"));
            // 加入排序：推荐好友优先，再按活跃度降序（可根据需求调整次要排序）
            queryWrapper.last("ORDER BY CASE WHEN id IN (" + recommendIdsStr + ") THEN 1 ELSE 0 END DESC, active_score DESC");
        } else {
            // 无推荐好友时，默认按活跃度降序（可根据需求调整）
            queryWrapper.last("ORDER BY RAND()");
        }

        // 4. 分页查询用户基础数据
        IPage<User> userPage = new Page<>(current, size);
        userPage = page(userPage, queryWrapper);
        List<User> userList = userPage.getRecords();
        if (CollUtil.isEmpty(userList)) {
            return new Page<>(current, size, 0);
        }

        // 5. 批量获取关联数据（避免N+1查询）
        List<String> userIds = userList.stream().map(User::getId).collect(Collectors.toList());
        // 5.1 在线用户ID列表（对接IM服务）
        Set<String> onlineUserIds = tencentImService.getOnlineUserIds();
        // 5.2 VIP用户ID列表
        Set<String> vipUserIds = userVipTimeService.list(new LambdaQueryWrapper<UserVipTime>()
                        .gt(UserVipTime::getEndTime, LocalDateTime.now())
                        .in(UserVipTime::getUserId, userIds)
                ).stream()
                .map(UserVipTime::getUserId)
                .collect(Collectors.toSet());
        // 5.3 资源/需求数量（批量统计）
        Map<String, Integer> resourceCountMap = countResourceOrDemand(userIds, DemandTypeEnum.OWN);
        Map<String, Integer> demandCountMap = countResourceOrDemand(userIds, DemandTypeEnum.NEED);
        // 获取好友关系状态
        List<FriendRelation> relations = friendRelationService.getFriendRelationsByUserId(StpUtil.getLoginIdAsString());

        Map<String, Integer> relationStatusMap = relations.stream()
                .collect(Collectors.toMap(FriendRelation::getFriendId, FriendRelation::getStatus));


        // 6. 转换为PublicFriendVO
        List<PublicFriendVO> friendVOList = userList.stream().map(user -> {
            PublicFriendVO vo = ConvertUtils.copyBean(user, PublicFriendVO.class);

            vo.setUserId(user.getId());
            vo.setImUserId(user.getImUserSig());
            if (CollUtil.isEmpty(user.getExtraPhones())) {
                vo.setContactMobile(user.getCellphone());
            } else {
                vo.setContactMobile(user.getExtraPhones().get(0));
            }
            // 6.1 地区拼接（省+市+区县）
            String region = StringUtils.join(
                    Arrays.asList(user.getProvince(), user.getCity(), user.getDistrict()),
                    ""
            );
            vo.setRegion(region);
            // 6.2 VIP状态
            vo.setIsVip(vipUserIds.contains(user.getId()));
            // 6.3 在线状态
            vo.setIsOnline(onlineUserIds.contains(user.getId()));
//            vo.setIsOnline(true);

            // 6.4 资源/需求数量
            vo.setResourceCount(resourceCountMap.getOrDefault(user.getId(), 0));
            vo.setDemandCount(demandCountMap.getOrDefault(user.getId(), 0));
            // 6.5 好友状态（默认未申请，后续可关联好友表扩展）
            // 设置关系状态
            Integer status = relationStatusMap.get(user.getId());
            if (status != null) {
                vo.setFriendStatus(status);
            } else {
                vo.setFriendStatus(-1);
            }
            return vo;
        }).collect(Collectors.toList());

        // 7. 构建并返回分页VO
        IPage<PublicFriendVO> friendPage = new Page<>(
                userPage.getCurrent(),
                userPage.getSize(),
                userPage.getTotal()
        );
        friendPage.setRecords(friendVOList);
        return friendPage;
    }

    /**
     * 搜索公开豹友（按昵称/手机号模糊匹配）
     */
    @Override
    public IPage<PublicFriendVO> searchPublicFriends(String loginUserId, String keyword, Long current, Long size) {
        // 1. 构建搜索查询条件（基础条件+关键词匹配）
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                .eq(User::getIsPublic, true)
                .eq(User::getDisEnable, false)
                .eq(User::getLogout, false)
                .ne(User::getId, loginUserId)
                // 模糊匹配昵称或手机号
                .and(w -> w.like(User::getName, keyword).or().like(User::getCellphone, keyword));

        // 2. 分页查询
        IPage<User> userPage = new Page<>(current, size);
        userPage = page(userPage, queryWrapper);
        List<User> userList = userPage.getRecords();
        if (CollUtil.isEmpty(userList)) {
            return new Page<>(current, size, 0);
        }

        // 3. 批量获取关联数据（同列表接口逻辑）
        List<String> userIds = userList.stream().map(User::getId).collect(Collectors.toList());
        Set<String> onlineUserIds = tencentImService.getOnlineUserIds();
        Set<String> vipUserIds = userVipTimeService.list(new LambdaQueryWrapper<UserVipTime>()
                        .gt(UserVipTime::getEndTime, LocalDateTime.now())
                        .in(UserVipTime::getUserId, userIds)
                ).stream()
                .map(UserVipTime::getUserId)
                .collect(Collectors.toSet());
        Map<String, Integer> resourceCountMap = countResourceOrDemand(userIds, DemandTypeEnum.OWN);
        Map<String, Integer> demandCountMap = countResourceOrDemand(userIds, DemandTypeEnum.NEED);
        // 获取好友关系状态
        List<FriendRelation> relations = friendRelationService.getFriendRelationsByUserId(StpUtil.getLoginIdAsString());

//        Map<String, Integer> relationStatusMap = relations.stream()
//                .collect(Collectors.toMap(FriendRelation::getFriendId, FriendRelation::getStatus));

        Map<String, Integer> relationStatusMap = relations.stream()
                .collect(Collectors.toMap(
                        FriendRelation::getFriendId,
                        FriendRelation::getStatus,
                        (existingValue, newValue) -> existingValue // 重复时保留旧值
                ));

        // 4. 转换VO
        List<PublicFriendVO> friendVOList = userList.stream().map(user -> {
            PublicFriendVO vo = ConvertUtils.copyBean(user, PublicFriendVO.class);
            String region = StringUtils.join(
                    Arrays.asList(user.getProvince(), user.getCity(), user.getDistrict()),
                    ""
            );

            vo.setUserId(user.getId());
            vo.setImUserId(user.getImUserSig());

            vo.setRegion(region);
            vo.setIsVip(vipUserIds.contains(user.getId()));
            vo.setIsOnline(onlineUserIds.contains(user.getId()));
//            vo.setIsOnline(true);
            vo.setResourceCount(resourceCountMap.getOrDefault(user.getId(), 0));
            vo.setDemandCount(demandCountMap.getOrDefault(user.getId(), 0));

            // 设置关系状态
            Integer status = relationStatusMap.get(user.getId());
            if (status != null) {
                vo.setFriendStatus(status);
            } else {
                vo.setFriendStatus(-1);
            }
            return vo;
        }).collect(Collectors.toList());

        // 5. 返回分页结果
        IPage<PublicFriendVO> friendPage = new Page<>(
                userPage.getCurrent(),
                userPage.getSize(),
                userPage.getTotal()
        );
        friendPage.setRecords(friendVOList);
        return friendPage;
    }

    /**
     * 工具方法：批量统计用户的资源/需求数量
     *
     * @param userIds 用户ID列表
     * @param type    类型（OWN=资源，NEED=需求）
     * @return key=用户ID，value=数量
     */
    private Map<String, Integer> countResourceOrDemand(List<String> userIds, DemandTypeEnum type) {
        if (CollUtil.isEmpty(userIds)) {
            return Collections.emptyMap();
        }

        // 批量统计每个用户的资源/需求数量
        QueryWrapper<Demand> queryWrapper = new QueryWrapper<>();
        // 1. 条件：用户ID在列表中、类型匹配、状态有效
        queryWrapper.in("user_id", userIds) // 对应Demand实体的userId字段（数据库列名，需与实体映射一致）
                .eq("type", type)       // 对应Demand实体的type字段
                .eq("status", 1)        // 仅统计有效状态的资源/需求
                .groupBy("user_id")     // 按用户ID分组
                .select("user_id AS userId, COUNT(*) AS count"); // 自定义查询：用户ID + 数量统计

        // 2. 执行查询，返回Map列表（key为列名，value为值）
        List<Map<String, Object>> countList = demandService.listMaps(queryWrapper);

        if (CollUtil.isEmpty(countList)) {
            return Collections.emptyMap();
        }

        // 转换为Map
        return countList.stream()
                .collect(Collectors.toMap(
                        map -> map.get("userId").toString(),
                        map -> Integer.parseInt(map.get("count").toString()),
                        (a, b) -> b
                ));
    }

    /**
     * 获取当前用户与目标用户列表的好友关系状态
     *
     * @param currentUserId 当前用户ID
     * @param friendUserIds 待校验的好友ID列表
     * @return 好友状态列表
     */
    @Override
    public List<FriendStatus> getFriendStatuses(String currentUserId, List<String> friendUserIds) {
        // 1. 查询当前用户的IM签名（从User表获取）
        User currentUser = getById(currentUserId);
        if (currentUser == null || StringUtils.isBlank(currentUser.getImUserSig())) {
            throw new RuntimeException("当前用户IM信息不存在");
        }
        String userSig = new TLSSigAPIv2(imConfig).genUserSig(currentUserId, 86400);
        // 2. 调用IM服务批量校验好友关系
        return tencentImService.batchCheckFriends(
                currentUserId,
                userSig,
                friendUserIds
        );
    }


    @Override
    public List<User> getUsersForScoreCalculation(int limit) {
        // 步骤1：查询未计算过推荐的用户（lastRecommendTime为null）
        LambdaQueryWrapper<User> uncalculatedWrapper = new LambdaQueryWrapper<User>()
                .isNull(User::getLastRecommendTime) // 未计算过推荐
                .orderByDesc(User::getRegisterTime) // 新用户优先
                .last("  LIMIT " + limit); // 限制数量
        List<User> uncalculatedUsers = baseMapper.selectList(uncalculatedWrapper);

        // 步骤2：若未计算用户不足，补充最近活跃用户（近7天登录）
        if (uncalculatedUsers.size() < limit) {
            int remaining = limit - uncalculatedUsers.size();
            LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7); // 近7天

            LambdaQueryWrapper<User> activeWrapper = new LambdaQueryWrapper<User>()
                    .isNotNull(User::getLastRecommendTime) // 仅保留“已计算过推荐”的用户（按需调整）
                    // 核心：通过 apply 直接拼接完整的 ORDER BY 排序逻辑，不使用 orderByDesc
                    .apply("ORDER BY " +
                                    "CASE WHEN last_login_time >= {0} THEN 1 ELSE 0 END DESC, " + // 近7天登录的排前面
                                    "last_login_time DESC " + // 同一优先级内，最新登录排前面
                                    "LIMIT " + remaining, // 限制补充数量
                            sevenDaysAgo); // 限制补充的数量

            List<User> activeUsers = baseMapper.selectList(activeWrapper);

            // 合并结果（去重，避免重复用户）
            Set<String> existingIds = uncalculatedUsers.stream()
                    .map(User::getId)
                    .collect(Collectors.toSet());
            for (User user : activeUsers) {
                if (!existingIds.contains(user.getId())) {
                    uncalculatedUsers.add(user);
                    existingIds.add(user.getId());
                    if (uncalculatedUsers.size() >= limit) {
                        break;
                    }
                }
            }
        }

        // 确保结果不超过limit
        return uncalculatedUsers.stream()
                .limit(limit)
                .collect(Collectors.toList());
    }

    /**
     * 2. 获取最近登录的用户ID列表（近7天登录）
     */
    @Override
    public List<String> selectRecentLoginUserIds(int recentHalf) {
        LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7); // 近7天

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .ge(User::getLastLoginTime, sevenDaysAgo) // 保留筛选条件：近7天登录
                // 核心：通过 apply 拼接排序和数量限制，替代 orderByDesc 和 last
                .apply("ORDER BY last_login_time DESC LIMIT " + recentHalf); // 限制数量

        // 只查询ID字段（优化性能）
        return baseMapper.selectList(wrapper).stream()
                .map(User::getId)
                .collect(Collectors.toList());
    }

    /**
     * 3. 获取未计算过推荐的用户ID列表（lastRecommendTime为null）
     */
    @Override
    public List<String> selectUncalculatedUserIds(int limit) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                // 核心：通过 apply 拼接排序优先级+数量限制，原筛选条件改为排序逻辑
                .last(" ORDER BY " +
                        // 1. 第一优先级：未计算过推荐的用户排前面（null=1，非null=0）
                        "CASE WHEN last_recommend_time IS NULL THEN 1 ELSE 0 END DESC, " +
                        // 2. 第二优先级：活跃用户（activeScore>0）排前面（是=1，否=0）
                        "CASE WHEN active_score > 0 THEN 1 ELSE 0 END DESC, " +
                        // 3. 第三优先级：新用户优先（注册时间降序）
                        "register_time DESC " +
                        // 4. 数量限制
                        "LIMIT " + limit);


        return baseMapper.selectList(wrapper).stream()
                .map(User::getId)
                .collect(Collectors.toList());
    }


    /**
     * 4. 获取其他活跃用户ID列表（排除最近登录的用户）
     */
    @Override
    public List<String> selectOtherActiveUserIds(int otherHalf, List<String> excludeIds) {
        if (excludeIds == null || excludeIds.isEmpty()) {
            excludeIds = new ArrayList<>();
        }

        LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7); // 近7天

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .notIn(excludeIds.size() > 0, User::getId, excludeIds) // 排除指定用户
                // 核心：通过apply拼接所有排序优先级+数量限制，原筛选条件转为排序逻辑
                .apply("ORDER BY " +
                                // 2. 第二优先级：活跃用户（activeScore>0）排前面（是=1，否=0）
                                "CASE WHEN active_score > 0 THEN 1 ELSE 0 END DESC, " +
                                // 3. 第三优先级：非近7天登录的用户排前面（lastLoginTime < sevenDaysAgo=1，否=0）
                                "CASE WHEN last_login_time < {0} THEN 1 ELSE 0 END DESC, " +
                                // 4. 第四优先级：活跃度高的优先（原排序逻辑保留）
                                "active_score DESC " +
                                // 5. 数量限制
                                "LIMIT " + otherHalf,
                        sevenDaysAgo); // 绑定{0}对应的参数（sevenDaysAgo）

        return baseMapper.selectList(wrapper).stream()
                .map(User::getId)
                .collect(Collectors.toList());
    }

    /**
     * 5. 更新用户的最后推荐计算时间
     */
    @Override
    public void updateLastRecommendTime(String targetUserId, LocalDateTime now) {
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
                .eq(User::getId, targetUserId) // 目标用户
                .set(User::getLastRecommendTime, now); // 更新最后推荐时间

        baseMapper.update(null, wrapper);
    }

    @Override
    public Response cancelAccount(String userId) {
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
                .eq(User::getId, userId) // 目标用户
                .set(User::getLogout, true); // 更新最后推荐时间

        baseMapper.update(null, wrapper);

        return Response.buildSuccess();
    }
}
